#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <string>
#include <string.h>
#include <sstream>
#include <limits>
#include <iterator>
#include <algorithm>
#include <numeric>
#include <utility>
#include <functional>
using namespace std;
typedef vector<short> vs;
typedef istream_iterator<char> iIterator;
typedef vector<short> Category;
typedef vector<short> Vshort;
typedef multimap<string, Category> Game;
const short bonus_score = 63;
const short bonus_points = 35;
const short max_score = 375;
short Scores[15];
stringstream oss(stringstream::in | stringstream::out);

class HungarianMethod
{
	private:
		static bool Step4(const std::vector<std::vector<short> > &C, std::vector<std::vector<short> > &M, std::vector<short> &rows_covered, std::vector<short> &cols_covered, short &zero_row, short &zero_col);
		static void Step5(std::vector<std::vector<short> > &M, std::vector<short> &rows_covered, std::vector<short> &cols_covered, short zero_row, short zero_col);
		static void Step6(std::vector<std::vector<short> > &C, const std::vector<short> &rows_covered, const std::vector<short> &cols_covered);

		// find a zero whose row and column are both uncovered, and record its index
		static void FindZero(const std::vector<std::vector<short> > &C, const std::vector<short> &rows_covered, const std::vector<short> &cols_covered, short &zero_row, short &zero_col);
		// find and return the column index of starred zero in row zero_row
		static short FindStar(const std::vector<std::vector<short> > &M, short zero_row);

	public:
		static std::vector<short> MunkresAlgorithm(const std::vector<std::vector<short> > &costs);
};


/**
 * \brief top level Munkres algorithm function
 */
vector<short> HungarianMethod::MunkresAlgorithm(const vector<vector<short> > &costs)
{
	const short N = costs.size();
	vector<vector<short> > C = costs;


	// step 1, subtract the minimum value of each row from each element in the row
	for(short i = 0; i < N; i++)
	{
		short minval = *std::min_element(C[i].begin(), C[i].end());
		for(short j = 0; j < N; j++)
			C[i][j] -= minval;
	}


	// step 2, star a zero in the cost matrix if there are no other zeros on the same row or column
	vector<vector<short> > M(N, vector<short>(N, 0));
	vector<short> rows_covered(N, 0);
	vector<short> cols_covered(N, 0);
	for(short i = 0; i < N; i++)
		for(short j = 0; j < N; j++)
			if(C[i][j] == 0 && rows_covered[i] == 0 && cols_covered[j] == 0)
			{
				M[i][j] = 1;
				rows_covered[i] = 1;
				cols_covered[j] = 1;
			}
	rows_covered = vector<short>(N, 0);
	cols_covered = vector<short>(N, 0);


	bool done = false;
	bool step3and5 = true;
	while(!done)
	{
		// step 3, cover all columns with starred zeros and terminate if solution is found
		if(step3and5)
		{
			for(short i = 0; i < N; i++)
				for(short j = 0; j < N; j++)
					if(M[i][j] == 1)
						cols_covered[j] = 1;

			// if the perfect solution found, terminate the algorithm
			short count = accumulate(cols_covered.begin(), cols_covered.end(), 0);
			if(count >= N)
			{
				done = true;
				break;
			}
		}


		// step 4, look for a noncovered zero and prime it
		short zero_row = -1;
		short zero_col = -1;
		step3and5 = Step4(C, M, rows_covered, cols_covered, zero_row, zero_col);
		short bah4 = 0;


		// step 5 or 6
		if(step3and5)
			Step5(M, rows_covered, cols_covered, zero_row, zero_col);
		else
			Step6(C, rows_covered, cols_covered);
	}


	// extract final mapping
	vector<short> cols(N, -1);
	for(short i = 0; i < N; i++)
		for(short j = 0; j < N; j++)
			if(M[i][j] == 1)
			{
				cols[i] = j;
				break;
			}

	return cols;
}

/**
 * \brief look for a noncovered zero and prime it
 */
bool HungarianMethod::Step4(const vector<vector<short> > &C, vector<vector<short> > &M, vector<short> &rows_covered, vector<short> &cols_covered, short &zero_row, short &zero_col)
{
	bool done = false;
	while(!done)
	{
		zero_row = -1;
		zero_col = -1;
		FindZero(C, rows_covered, cols_covered, zero_row, zero_col);

		if(zero_row < 0)	// no uncovered zero found
		{
			done = true;
			return false;	// should go to step 6
		}
		else
		{
			M[zero_row][zero_col] = 2;	// prime the zero;

			short star_col = FindStar(M, zero_row);
			if(star_col >= 0)
			// find an starred zero on row zero_row, cover row zero_row and uncover column star_col
			{
				rows_covered[zero_row] = 1;
				cols_covered[star_col] = 0;
			}
			else
				done = true;	// should go to step 5
		}
	}

	return true; // should go to step 5
}

/**
 * \brief construct alternating series of primed and starred zeros
 */
void HungarianMethod::Step5(vector<vector<short> > &M, vector<short> &rows_covered, vector<short> &cols_covered, short zero_row, short zero_col)
{
	const short N = M.size();

	vector<short> stars_in_col(1, zero_row);
	vector<short> primes_in_row(1, zero_col);

	bool done = false;
	while(!done)		// terminate at a primed zero with starred zero in its column
	{
		// find starred zero in the column
		short star_row = -1;
		short curr_col = primes_in_row.back();
		for(short i = 0; i < N; i++)
			if(M[i][curr_col] == 1)
			{
				star_row = i;
				break;
			}

		if(star_row >= 0)
		{
			stars_in_col.push_back(star_row);
			primes_in_row.push_back(primes_in_row.back());
		}
		else
			done = true;

		if(!done)
		{
			// find primed zero in the row
			short curr_row = stars_in_col.back();
			short prime_col = -1;
			for(short j = 0; j < N; j++)
				if(M[curr_row][j] == 2)
				{
					prime_col = j;
					break;
				}

			stars_in_col.push_back(stars_in_col.back());
			primes_in_row.push_back(prime_col);
		}
	}

	// convert path, unstar starred zeros, and star primed zeros
	for(unsigned short i = 0; i < stars_in_col.size(); i++)
	{
		short r = stars_in_col[i];
		short c = primes_in_row[i];
		if(M[r][c] == 1)
			M[r][c] = 0;
		else
			M[r][c] = 1;
	}
	// clear covers
	rows_covered = vector<short>(rows_covered.size(), 0);
	cols_covered = vector<short>(cols_covered.size(), 0);
	// unprime remaining primed zeros
	for(short i = 0; i < N; i++)
		for(short j = 0; j < N; j++)
			if(M[i][j] == 2)
				M[i][j] = 0;

	// should return to step 3
}

/**
 * \brief subtract the minimum uncovered value from covered rows and add it to uncovered columns
 */
void HungarianMethod::Step6(vector<vector<short> > &C, const vector<short> &rows_covered, const vector<short> &cols_covered)
{
	const short N = C.size();

	// find minimum uncovered value
	short minval = numeric_limits<short>::max();
	for(short j = 0; j < N; j++)
		if(cols_covered[j] == 0)
			for(short i = 0; i < N; i++)
				if(rows_covered[i] == 0)
					if(C[i][j] < minval)
						minval = C[i][j];

	// subtract it from covered rows and add it to uncovered columns
	for(short i = 0; i < N; i++)
		for(short j = 0; j < N; j++)
		{
			if(rows_covered[i] == 1)
				C[i][j] += minval;
			if(cols_covered[j] == 0)
				C[i][j] -= minval;
		}

	// should return to step 4
}


/**
 * \brief find a zero whose row and column are both uncovered, and record its index
 */
void HungarianMethod::FindZero(const vector<vector<short> > &C, const vector<short> &rows_covered, const vector<short> &cols_covered, short &zero_row, short &zero_col)
{
	const short N = C.size();

	zero_row = -1;
	zero_col = -1;
	bool found_zero = false;
	for(short j = 0; j < N; j++)
	{
		if(cols_covered[j] == 0)
			for(short i = 0; i < N; i++)
				if(rows_covered[i] == 0)
					if(C[i][j] == 0)
					{
						zero_row = i;
						zero_col = j;
						found_zero = true;
						break;
					}
		if(found_zero)
			break;
	}
}


/**
 * \brief find and return the column index of starred zero in row zero_row
 */
short HungarianMethod::FindStar(const vector<vector<short> > &M, short zero_row)
{
	const short N = M.size();

	for(short j = 0; j < N; j++)
		if(M[zero_row][j] == 1)
			return j;

	return -1;
}

void analogous_code(const string& word, string& code) {
	code.clear();
	string::const_iterator w = word.begin();
	map<char, short> hash;
	map<char, short>::iterator it;
	while(w != word.end()) {
		it = hash.find(*w);
		if(it == hash.end()) {
			pair<char, short>p(*w, 1);
			hash.insert(hash.end(), p);
		} else {
			it->second++;
		}
		w++;
	}
	for(short i =0; i < word.size(); i++) {
		code += (char)((short)hash[word[i]]+(short)'0');
	}
	sort(code.begin(), code.end(), greater<char>());
}

/*
   ones - sum of all ones thrown
   twos - sum of all twos thrown
   threes - sum of all threes thrown
   fours - sum of all fours thrown
   fives - sum of all fives thrown
   sixes - sum of all sixes thrown
   chance - sum of all dice
   three of a kind - sum of all dice, provided at least three have same value
   four of a kind - sum of all dice, provided at least four have same value
   five of a kind - 50 points, provided all five dice have same value
   short straight - 25 points, provided four of the dice form a sequence (that is, 1,2,3,4 or 2,3,4,5 or 3,4,5,6)
   long straight - 35 points, provided all dice form a sequence (1,2,3,4,5 or 2,3,4,5,6)
   full house -40 points, provided three of the dice are equal and the other two dice are also equal.
   (for example, 2,2,5,5,5)
   */
short sum_all(const string& dices) {
	return (accumulate(dices.begin(), dices.end(), 0) - (48*5));
}

short face(const string& dices, char head) {
	short score = 0;	
	size_t it = dices.find(head);
	while(it < dices.size() && dices.at(it) == head) {
		score+=(head-48);
		it++;
	}
	return score;
}

short three_of_a_kind(const string& dices, const string& code) {
	if(code == "33322" || code == "33311" || code == "44441" || code == "55555") {
		return sum_all(dices);
	} else {
		return 0;
	}	
}

short four_of_a_kind(const string& dices, const string& code) {
	if(code == "44441" || code == "55555") {
		return sum_all(dices);
	} else {
		return 0;	
	}	
}

short five_of_a_kind(const string& code) {
	return ((code == "55555") ? 50 : 0);	
}

short full_house(const string& code) {
	return ((code == "33322" || code == "55555") ? 40 : 0);	
}

short short_straight(const string& dices, const string& code) {
	if(code == "22111" || code == "11111") {
		char start = dices[0]; 
		if(start-1 != dices[1]) {
			start = dices[1];
		}
		short count = 3;
		while(count-- > 0) {
			if(dices.find(--start) == string::npos)
				return 0;
		}
		return 25;
	}
	return 0;
}

short long_straight(const string& dices) {
	return ((dices == "65432") || (dices == "54321") ? 35 : 0);	
}

enum {
	CHOSEN = 0,
	FACE1 = 1,		
	FACE2 = 2,		
	FACE3 = 3,		
	FACE4 = 4,		
	FACE5 = 5,		
	FACE6 = 6,
	CHANCE = 7,
	K3 = 8,
	K4 = 9,
	K5 = 10,
	SS = 11,
	LS = 12,
	FULL = 13,
	BONUS = 14,
	SUM = 15
};

void get_category(const string& dices, Category& score_list) {
	string code;
	analogous_code(dices, code);
	for(short i = 1; i <= 13; i++) {
		short score = 0;
		switch(i) {
			case FACE1:
				score = face(dices, '1');
				break;
			case FACE2:
				score = face(dices, '2');
				break;
			case FACE3:
				score = face(dices, '3');
				break;
			case FACE4:
				score = face(dices, '4');
				break;
			case FACE5:
				score = face(dices, '5');
				break;
			case FACE6:
				score = face(dices, '6');
				break;
			case CHANCE:
				score = sum_all(dices);
				break;
			case K3:
				score = three_of_a_kind(dices, code);
				break;
			case K4:
				score = four_of_a_kind(dices, code);
				break;
			case K5:
				score = five_of_a_kind(code);
				break;
			case FULL:
				score = full_house(code);
				break;
			case SS:
				score = short_straight(dices, code);
				break;
			case LS:
				score = long_straight(dices);
				break;
			default:
				break;
		}
		score_list.push_back(score);
	}
}

void read_game(Game& game) {
	short throw_count = 0;
	while(++throw_count <= 13) {
		oss.clear();
		string line, round;
		getline(cin, line);
		oss << line;
		copy(iIterator(oss), iIterator(), back_inserter(round));
		if(round.empty() == false) {
			sort(round.begin(), round.end(), greater<char>());
			Category score_list;
			score_list.push_back(0);
			get_category(round, score_list);
			game.insert(game.end(), make_pair(round, score_list));
		}
	}
}

short Bonus_Sum() {
	return accumulate(Scores, Scores+6, 0);	
}

short isBonus() {
	return accumulate(Scores, Scores+6, 0) >= bonus_score ? bonus_points : 0;	
}

short Total(short* sc) {
	return accumulate(sc, sc+14, 0);	
}


void print_game(const Game& game) {
	Game::const_iterator it = game.begin();
	cout <<endl;
	while(it != game.end()) {
		cout << it->first << " : ";
		copy(it->second.begin(), it->second.end(), ostream_iterator<short>(cout, "\t"));
		cout <<endl;
		it++;	
	}
	cout <<endl;
}

void choose_bet(Game& game, short bet) {
	Game::iterator it = game.begin();
	Game::iterator best = game.end();
	while(it != game.end()) {
		if(best == game.end() && it->second[CHOSEN] == 0) {
			best = it;	
		} else if(best != game.end() && it->second[bet] > best->second[bet] && it->second[CHOSEN] ==0) {
			best = it;
		} else if(best != game.end() && it->second[bet] == best->second[bet] &&\
				it->second[CHOSEN] ==0 && it->second[CHANCE] < best->second[CHANCE]) {
			best = it;
		}
		it++;
	}
	if(best != game.end() && best->second[bet] != 0) {
		best->second[CHOSEN] = bet;
		Scores[bet-1] = best->second[bet];
	}	
}

void clear_bets(Game& game, short start=FACE1, short end=FACE6) {
	Game::iterator it = game.begin();
	while(it != game.end()) {
		if(it->second[CHOSEN] >= start && it->second[CHOSEN] <= end) {
			Scores[it->second[CHOSEN]-1] = 0;
			it->second[CHOSEN] = CHOSEN;
		}
		it++;
	}
}

void hungarian_assignment(const vector<Vshort>& hash, vector<short>& positions) {
	positions = HungarianMethod::MunkresAlgorithm(hash);
}

void update_score_from_position(const vector<Game::iterator>& column, const vector<short>& positions) {
	for(short i = 0; i < FACE6 && i < positions.size(); ++i) {
		column[positions[i]]->second[0] = FACE1+i;
		Scores[FACE1+i-1] = column[positions[i]]->second[FACE1+i];
	}
}

short current_sum(const vector<Game::iterator>& column, const vector<short>& positions) {
	short sum = 0;
	for(short i = 0; i < FACE6 && i < positions.size(); ++i) 
		sum += column[positions[i]]->second[FACE1+i];
	return sum;	
}

void search_hungarian_match(const vector<Vshort>& hash,\
		const vector<Game::iterator>& column,\
		vector<short>& in, bool max = false)
{
	hungarian_assignment(hash, in);
	short sum = current_sum(column, in);
	if(sum <= bonus_score || max == true) 
		return;
	short previous_sum = sum;
	vector<Vshort> rehash;
	bool found = false;
	bool again = true;
	vector<short> out(in);
	while (found == false && again == true) {
		again = false;
		for(short i = 0; i < out.size(); i++) {
			rehash = hash;
			rehash[i][out[i]] = max_score;
			hungarian_assignment(rehash, in);
			sum = current_sum(column, in);
			if(sum == bonus_score) {
				found = true;
				out = in;
				break;
			} else if(sum > bonus_score && sum < previous_sum) {
				out = in;
				previous_sum = sum;
				again = true;
				break;
			}
		}
	}
	in = out;
}

bool choose_face_bet(const vector<Vshort>& hash, const vector<Game::iterator>& column, bool max=false) {
	vector<short> out(hash.size());
	search_hungarian_match(hash, column, out, max);
	update_score_from_position(column, out);
	return isBonus() != 0;
}

bool choose_face_bet(Game & game, short include_chance=false, bool max=false) {
	short rows = FACE6+include_chance;
	vector<Vshort> transpose_hash(rows);
	vector<Game::iterator> column;
	Game::iterator it = game.begin();
	while(it != game.end()) {
		if(it->second[CHOSEN] == CHOSEN) {
			for(short i = 0; i < rows; i++)
				transpose_hash[i].push_back(max_score - *(it->second.begin()+1+i));
			column.push_back(it);
		}
		it++;
	}
	// Make matrix N*N
	for(short i = rows; i < transpose_hash[0].size(); ++i) 
		transpose_hash.push_back(Vshort(transpose_hash[0].size(), max_score));
	return choose_face_bet(transpose_hash, column, max);
}

// Min Score 5
// Max Score 375 - > 11111, 22222, 33333, 44444, 55555, 66666, 66666, 
// 66666, 66666, *(66666, 66666), *(12345, 12345)
// Oreder - SS, LS, 5k, 6s, 5s , 4s, 3s, 2s, 1s, 4k, 3k, FULL, C 
void Yahtzee(Game& game) {
	short prev_scores[15];
	if(choose_face_bet(game) == true) { 
		choose_bet(game, LS);
		choose_bet(game, SS);
		choose_bet(game, K5);
		choose_bet(game, FULL);
		choose_bet(game, K4);
		choose_bet(game, K3);
		choose_bet(game, CHANCE);
		Scores[BONUS-1] = bonus_points;

		memcpy(prev_scores, Scores, sizeof(short)*15);
		clear_bets(game, FACE1, FULL);
		Scores[BONUS-1] = 0;

		choose_bet(game, LS);
		choose_bet(game, SS);
		choose_bet(game, K5);
		choose_bet(game, FULL);
		if(choose_face_bet(game, true, true) == true) {
			Scores[BONUS-1] = bonus_points;
		} 
		choose_bet(game, K4);
		choose_bet(game, K3);
		choose_bet(game, CHANCE);

		if (Total(Scores) < Total(prev_scores)){
			memcpy(Scores, prev_scores, sizeof(short)*15);
		}

	} else {
		clear_bets(game);
		choose_bet(game, LS);
		choose_bet(game, SS);
		choose_bet(game, K5);
		choose_bet(game, FULL);
		choose_bet(game, K4);
		choose_bet(game, K3);
		choose_face_bet(game, true);
		choose_bet(game, CHANCE);

		memcpy(prev_scores, Scores, sizeof(short)*15);
		clear_bets(game, FACE1, K4);

		choose_face_bet(game);
		choose_bet(game, K4);
		choose_bet(game, K3);
		choose_bet(game, CHANCE);

		if (Total(Scores) < Total(prev_scores)){
			memcpy(Scores, prev_scores, sizeof(short)*15);
		}

	}
	Scores[SUM-1] = Total(Scores);
}

int main() {
	while(cin.eof() == false) {
		memset(Scores, 0, sizeof(short)*15);
		Game game;
		read_game(game);
		if(game.size() == 13) {
			Yahtzee(game);
			copy(Scores, Scores+14, ostream_iterator<short>(cout, " "));
			cout << Scores[14] << endl;
		}
		//print_game(game);
	}
	return 0;	
}
